package device

import (
	"context"
	innerMiddleware "gitee.com/guolianyu/kit/skit/middleware"
	"github.com/go-kratos/kratos/v2/metadata"
	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/transport"
	"github.com/go-kratos/kratos/v2/transport/http"
	"regexp"
	"strconv"
	"strings"
)

type (
	Option func(*options)

	options struct {
	}
)
type Device struct {
	RemoteIP   string
	RequestID  string
	DeviceType string
	IsMobile   bool
}

type deviceKey struct{}

const HeaderRequestId = "X-Request-Id"

const ClientIpMetaKey = innerMiddleware.GlobalKeyPrefix + "client-ip"
const ClientIsMobileMetaKey = innerMiddleware.GlobalKeyPrefix + "is-mobile"

func Server(opts ...Option) middleware.Middleware {
	o := &options{}
	for _, opt := range opts {
		if opt != nil {
			opt(o)
		}
	}
	return func(handler middleware.Handler) middleware.Handler {
		return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
			if tr, ok := transport.FromServerContext(ctx); ok {
				if md, ok := metadata.FromServerContext(ctx); ok {
					//获取客户端IP
					ip := md.Get(ClientIpMetaKey)
					if len(ip) == 0 {
						if ht, ok := http.RequestFromServerContext(ctx); ok {
							ip = realIP(ht)
						}
					}
					//获取请求ID
					rid := md.Get(HeaderRequestId)
					if len(rid) == 0 {
						if ht, ok := http.RequestFromServerContext(ctx); ok {
							rid = requestID(ctx, ht)
						}
					}
					var isMobile = false
					isMobileStr := md.Get(ClientIsMobileMetaKey)
					if len(isMobileStr) == 0 {
						if ht, ok := http.RequestFromServerContext(ctx); ok {
							isMobile = IsMobile(ht)
						}
					} else {
						isMobile, _ = strconv.ParseBool(md.Get(ClientIsMobileMetaKey))
					}

					d := Device{
						RemoteIP:  ip,
						RequestID: rid,
						IsMobile:  isMobile,
					}
					tr.ReplyHeader().Set(HeaderRequestId, rid)
					ctx = NewContext(ctx, d)
					return handler(ctx, req)
				}
			}
			return handler(ctx, req)
		}
	}
}

// NewContext put auth info into context
func NewContext(ctx context.Context, d Device) context.Context {
	ctx = metadata.AppendToClientContext(ctx, ClientIpMetaKey, d.RemoteIP)
	ctx = metadata.AppendToClientContext(ctx, HeaderRequestId, d.RequestID)
	ctx = metadata.AppendToClientContext(ctx, ClientIsMobileMetaKey, strconv.FormatBool(d.IsMobile))
	return context.WithValue(ctx, deviceKey{}, d)
}

func FromContext(ctx context.Context) Device {
	d := ctx.Value(deviceKey{})
	if d != nil {
		return d.(Device)
	}
	return Device{}
}

func IsMobile(r *http.Request) bool {
	//put headers in a map
	headers := make(map[string]string)

	//net/http中获取headers
	if len(r.Header) > 0 {
		for k, v := range r.Header {
			headers[k] = v[0]
		}
	}
	var isMobile = false
	via := strings.ToLower(headers["VIA"])
	accept := strings.ToUpper(headers["Accept"])
	HttpXWapProfile := headers["X_WAP_PROFILE"]
	HttpProfile := headers["PROFILE"]
	HttpUserAgent := headers["User-Agent"]
	if via != "" && strings.Index(via, "wap") != -1 {
		isMobile = true
	} else if accept != "" && strings.Index(accept, "VND.WAP.WML") != -1 {
		isMobile = true
	} else if HttpXWapProfile != "" || HttpProfile != "" {
		isMobile = true
	} else if HttpUserAgent != "" {
		reg := regexp.MustCompile(`(?i:(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_|MicroMessenger))`)

		if len(reg.FindAllString(HttpUserAgent, -1)) > 0 {
			isMobile = true
		}
	}
	return isMobile
}
